/*
 * TextServices / TSDotNet / TSNative
 * Marco Maria Bellocchi, Mauro Bampo
 * Copyright(c) 2013.
 * 
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

// TextServicesTests1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
//#include <vld.h>
using namespace std;
using namespace TSNative::Filters;
using namespace TSNative::Services;

void TestCaesarCipher1() {
	string test1("551893,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

	string input = test1;

	CaesarCipher* pCipher1 = new CaesarCipher(1);
	CaesarCipher* pCipher2 = new CaesarCipher(2);
	CaesarCipher* pCipher3 = new CaesarCipher(-3);
	vector<Filter*> filters1;
	filters1.push_back(pCipher1);
	filters1.push_back(pCipher2);
	filters1.push_back(pCipher3);
	Processor* pProcessor = new PipelineProcessor(filters1);
	string output;
	bool result = true;
	for(int i =0;i<3;i++) {
		output = pProcessor->Process(input);
		result &= (output.compare(input) == 0);
	}

	cout << "TestCaesarCipher1: " << (result ? "OK" : "FAIL") << endl << endl;

	delete pProcessor;
}

void TestDictionaryFilter1() {

	int offset = 35;
	char dict1[256];
	char dict2[256];
	for(int i=0; i<256; i++) {
		dict1[i] = (i + offset) % 256;
		dict2[i] = (i - offset) % 256;
	}

	string test1("551893,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

	string input = test1;

	Filter* pFilter1 = new DictionaryFilter(dict1);
	Filter* pFilter2 = new DictionaryFilter(dict2);
	vector<Filter*> filters1;
	filters1.push_back(pFilter1);
	filters1.push_back(pFilter2);
	Processor* pProcessor = new PipelineProcessor(filters1);
	string output;
	bool result = true;
	for(int i =0;i<3;i++) {
		output = pProcessor->Process(input);
		result &= (output.compare(input) == 0);
	}

	cout << "TestDictionaryFilter1: " << (result ? "OK" : "FAIL") << endl << endl;

	delete pProcessor;
}

void TestNumberRLEEncoder1() {	
	vector<string> inputs;

	inputs.push_back("0,0");
    inputs.push_back("0,0,0,");
	inputs.push_back("+0,0,0");
    inputs.push_back("-0,0,0,");
	//inputs.push_back("3-0,0,0");  // BAD_STRING
	inputs.push_back("3,-0,-0,+0");
	inputs.push_back("3,-1,0,+0");
	inputs.push_back("3,+0,-0,+0");
	inputs.push_back("3,+1,0,+0");
	inputs.push_back("3,.345,.678");
	inputs.push_back("3,.34500,.678");
	inputs.push_back("23,0,0,-396.393,0,0,646,0,0,");
	inputs.push_back("551893,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
	inputs.push_back("551893,626469,-677.338,.396393,0,0,552012,0,352,-.0538,0,0027963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000.4323,0,0,0,");

	CsvNumRleEncoder* pEnc = new CsvNumRleEncoder();
	vector<Filter*> filters1;
	filters1.push_back(pEnc);
	Processor* pProcessor1 = new PipelineProcessor(filters1);

	CsvNumRleDecoder* pDec = new CsvNumRleDecoder();
	vector<Filter*> filters2;
	filters2.push_back(pDec);
	Processor* pProcessor2 = new PipelineProcessor(filters2);

	string output1, output2;
	bool result = true;
	int nIter = 1e0/inputs.size() + 1;
	for(int j=0; j<nIter;j++)
		for(int i =0;i<inputs.size();i++) {
			// Not using a pipeline here, in order to be able to catch the output of encoding
			output1 = pProcessor1->Process(inputs[i]);
			output2 = pProcessor2->Process(output1);
			if(nIter == 1)
				cout << "TestNumberRLEEncoder1: " << endl << inputs[i] << endl << " -> " << endl << output1 << endl << " -> " << endl << output2  << endl << endl;
		}

		delete pProcessor1;
		delete pProcessor2;
}

void TestCharToNibbleEncoder1() {

	int offset = 35;
	char dictionary[16] = { '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', ',', '.', 'Z', '$' };

	string test1("55189,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
	string test2("551893,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

	vector<string> inputs;
	inputs.push_back(test1);
	inputs.push_back(test2);

	Filter* pFilter1 = new CharToNibbleEncoder(dictionary);
	Filter* pFilter2 = new CharToNibbleDecoder(dictionary);
	vector<Filter*> filters1;
	vector<Filter*> filters2;
	filters1.push_back(pFilter1);
	filters2.push_back(pFilter2);
	Processor* pProcessor1 = new PipelineProcessor(filters1);
	Processor* pProcessor2 = new PipelineProcessor(filters2);
	string output1, output2;
	bool result = true;
	for(int i =0;i<3;i++) {
		for(int j=0; j<inputs.size(); j++) {
			// Not using a pipeline here, in order to be able to catch the output of encoding
			output1 = pProcessor1->Process(inputs[j]);
			output2 = pProcessor2->Process(output1);
			result &= (output2.compare(inputs[j]) == 0);
			if(i == 1)
				cout << "TestCharToNibbleEncoder1: " << endl << inputs[j] << endl << " -> " << endl << output1 << endl << " -> " << endl << output2  << endl << endl;
		}
	}

	cout << "TestCharToNibbleEncoder1: " << (result ? "OK" : "FAIL") << endl << endl;

	delete pProcessor1, pProcessor2;
}

void TestSqlCsvCompressor1() {

	string test0("23,0,0");  // L = 6
	string test1("23,0,0,-396.393,0,0,646,0,0,");
	string test2("551893,626469,677338,396393,552012,352538,27963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
	string test3("55189,626469,-677.338,.396393,0,0,552012,0,352,-.0538,0,0027963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000.4323,0,0,0,");
	string test4("551893,626469,-677.338,.396393,0,0,552012,0,352,-.0538,0,0027963,-33350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000.4323,0,0,0,");
	string test5("0,0,-421.84232,-2238.431812,-7700.622324,-13394.071594,-25206.111758,-31215.922354,-41334.54218,-51816.167462,-66504.590744,-76135.131954,-92066.85092,-108795.383934,-117840.003658,-121399.473068,-130803.016416,-137750.956066,-148581.152292,-162206.598108,-174252.120814,-171964.879214,-176701.18506,-176885.451566,-181012.944746,-196608.356758,-205358.963636,-266085.341904,-303523.967672,-342422.403662,-374867.27061,-401375.034054,-457094.579386,-502268.466202,-526026.073054,-552884.766906,-567840.806426,-564791.081126,-593550.873594,-632752.471606,-678405.355556,-709651.498896,-748208.740946,-768746.908636,-744767.423288,-773808.877464,-822058.04012,-830124.21368,-883389.536804,-854137.863972,-893386.964366,-881535.718946,-912609.823238,-896619.890396,-928736.538774,-927957.75081,-944515.375324,-962142.701666,-1014604.498522,-1019191.208438,-1027119.38494,-989404.805202,-1008495.278566,-972736.1269,-885537.13764,-712208.111522,-539175.584218,-374383.347286,-207621.239086,-111942.116324,-40685.55953,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

	vector<string> inputs;
	inputs.push_back(test1);
	inputs.push_back(test2);
	inputs.push_back(test3);
	inputs.push_back(test4);
	inputs.push_back(test5);
	inputs.clear();      // <--------
	inputs.push_back(test5);

	CsvCompressorOptions options = (CsvCompressorOptions) (
		CsvCompressorOptions::None
		| CsvCompressorOptions::EnableRle
		| CsvCompressorOptions::EnableCharToNibble
		| CsvCompressorOptions::EnableBinToAlphabet
		);
	AlphabetCsvCompressor sqlCompressor(options);

	string output1, output2;
	bool result = true;
	for(int i = 1; i < 1e6; i++) {
		for(int j=0; j<inputs.size(); j++) {
			// Not using a pipeline here, in order to be able to catch the output of encoding
			output1 = sqlCompressor.Encode(inputs[j]);
			output2 = sqlCompressor.Decode(output1);
			result &= (output2.compare(inputs[j]) == 0);
			if(i == 0)
				cout << "TestSqlCsvCompressor1: " << endl << inputs[j] << endl << " -> " << endl << output1 << endl << " -> " << endl << output2  << endl << endl;
		}
	}

	cout << "TestSqlCsvCompressor1: " << (result ? "OK" : "FAIL") << endl << endl;
}

void TestSqlCsvCompressor2_ForIntegration() {

	vector<string> inputs;
	inputs.push_back("0,1,0");
	inputs.push_back("0,1,0,0");
	inputs.push_back("0,1,0,0,0");
	inputs.push_back("0,0,1,0,0,0");
	inputs.push_back("-0,0,1,0,0,0");
	inputs.push_back("+0,0,1,0,0,0");
	inputs.push_back("0,0,0,1,0,0,0");
	inputs.push_back("0,+0,0,1,0,0,0");
	inputs.push_back("0,-0,0,1,0,0,0");
	inputs.push_back("0,0,0,+1,0,0,0");
	inputs.push_back("0,0,0,-1,0,0,0");
	inputs.push_back("0,0,-421.84232,-2238.431812,-7700.622324,-13394.071594,-25206.111758,-31215.922354,-41334.54218,-51816.167462,-66504.590744,-76135.131954,-92066.85092,-108795.383934,-117840.003658,-121399.473068,-130803.016416,-137750.956066,-148581.152292,-162206.598108,-174252.120814,-171964.879214,-176701.18506,-176885.451566,-181012.944746,-196608.356758,-205358.963636,-266085.341904,-303523.967672,-342422.403662,-374867.27061,-401375.034054,-457094.579386,-502268.466202,-526026.073054,-552884.766906,-567840.806426,-564791.081126,-593550.873594,-632752.471606,-678405.355556,-709651.498896,-748208.740946,-768746.908636,-744767.423288,-773808.877464,-822058.04012,-830124.21368,-883389.536804,-854137.863972,-893386.964366,-881535.718946,-912609.823238,-896619.890396,-928736.538774,-927957.75081,-944515.375324,-962142.701666,-1014604.498522,-1019191.208438,-1027119.38494,-989404.805202,-1008495.278566,-972736.1269,-885537.13764,-712208.111522,-539175.584218,-374383.347286,-207621.239086,-111942.116324,-40685.55953,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

	bool result = true;
	for(int j = 0; j < inputs.size(); j++) {
		cout << inputs[j];

		CsvCompressorOptions options = CsvCompressorOptions::None;
		for(int stage = 0; stage<3;stage++) {

			options = (CsvCompressorOptions) (options | (1 << stage));

			AlphabetCsvCompressor sqlCompressor(options);
			string output1, output2;
			// Not using a pipeline here, in order to be able to catch the output of encoding
			output1 = sqlCompressor.Encode(inputs[j]);
			output2 = sqlCompressor.Decode(output1);
			result &= (output2.compare(inputs[j]) == 0);
			cout << "; " << output1;
			if(stage+1 == 3)
				cout << "; " << output2;
		}
		cout << endl;
	}

	cout << "TestSqlCsvCompressor2_ForIntegration: " << (result ? "OK" : "FAIL") << endl << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	DECLARE_TIMING(myTimer);
	START_TIMING(myTimer);
	//TestCaesarCipher1();
	//TestDictionaryFilter1();
	//TestNumberRLEEncoder1();
	//TestCharToNibbleEncoder1();
	TestSqlCsvCompressor2_ForIntegration();
	STOP_TIMING(myTimer);
	printf("Execution time: %f ms.\n", GET_TIMING(myTimer) );

	return 0;
}
